﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;

using Robert.TwitBy.Models;
using Robert.TwitBy.Properties;
using Robert.TwitBy.Callers;

using TweetSharp.Model;
using TweetSharp.Twitter.Extensions;
using TweetSharp.Twitter.Fluent;
using TweetSharp.Twitter.Model;
using System.Windows.Markup;


namespace Robert.TwitBy.ViewModels
{
    public class TwitterViewModel: MainViewModel
    {
        public TwitterViewModel(Dispatcher dispatcher, MainViewModel model):
            base(dispatcher,model)
        {
            _dispatcher = dispatcher;

            _timer = new DispatcherTimer(DispatcherPriority.ApplicationIdle, _dispatcher);
            _timer.Interval = this.RefreshInterval;
            _timer.Tick += new EventHandler(_timer_Tick);

            _model = model;

            _hCaller = new HomeCaller(this);
            _yCaller = new YourCaller(this);
            _mCaller = new MentionsCaller(this);
            _dCaller = new DirectsCaller(this);

            this.GoToSettingsCommand = new RelayCommand(p => _model.GoToSettings(new SettingsViewModel(_dispatcher, _model)));
            this.HashtagCommand = new RelayCommand(p => this.SearchForHash(p as string));
            this.HyperlinkCommand = new RelayCommand(p => this.GoToUrl(p as string));
            this.LoadDirectsCommand = new RelayCommand(p => this.LoadYourDirectsTimeline(), p => this.CanRefresh());
            this.LoadHomeCommand = new RelayCommand(p => this.LoadHomeTimeline(), p => this.CanRefresh());
            this.LoadMentionsCommand = new RelayCommand(p => this.LoadMentionsTimeline(), p => this.CanRefresh());
            this.LoadYourCommand = new RelayCommand(p => this.LoadYourTweetsTimeline(), p => this.CanRefresh());
            this.LogOutCommand = new RelayCommand(p => this.LogOut());
            this.MentionCommand = new RelayCommand(p => this.Mention(p as string));
            this.RefreshTimelineCommand = new RelayCommand(p => this.Refresh(), p => this.CanRefresh());
            this.UpdateCommand = new RelayCommand(p => new Action(() => this.SendTweet()).BeginInvoke(null, null), p => this.CanSendTweet());
            this.RetweetCommand = new RelayCommand(p => this.Retweet(this.SelectedItem), p => this.CanUseToolbar());
            this.DeleteCommand = new RelayCommand(p => this.Delete(this.SelectedItem), p => this.CanDelete(this.SelectedItem));
            this.FavoriteCommand = new RelayCommand(p => this.Favorite(this.SelectedItem), p => this.CanUseToolbar());
            this.ReplyToCommand = new RelayCommand(p => this.ReplyTo(this.SelectedItem), p => this.CanUseToolbar());
            this.DirectMessageCommand = new RelayCommand(p => new Action(() => this.DirectMessage(this.SelectedItem)).BeginInvoke(null, null), p => this.CanDirectMessage());
            this.UpdateFollowersCommand = new RelayCommand(p => this.UpdateFollowers());
            

            this.InputBindings = new List<InputBinding>();
            this.InputBindings.Add(new KeyBinding(this.RefreshTimelineCommand,new KeyGesture(Key.F5,ModifierKeys.Control)));
            this.InputBindings.Add(new KeyBinding(this.UpdateCommand,new KeyGesture(Key.Enter)));
            this.InputBindings.Add(new KeyBinding(this.LoadHomeCommand, new KeyGesture(Key.D1, ModifierKeys.Control)));
            this.InputBindings.Add(new KeyBinding(this.LoadMentionsCommand, new KeyGesture(Key.D3, ModifierKeys.Control)));
            this.InputBindings.Add(new KeyBinding(this.LoadYourCommand, new KeyGesture(Key.D2, ModifierKeys.Control)));
            this.InputBindings.Add(new KeyBinding(this.LoadDirectsCommand, new KeyGesture(Key.D4, ModifierKeys.Control)));
            this.InputBindings.Add(new KeyBinding(this.ReplyToCommand, new KeyGesture(Key.R, ModifierKeys.Control)));
            this.InputBindings.Add(new KeyBinding(this.FavoriteCommand, new KeyGesture(Key.F, ModifierKeys.Control)));
            this.InputBindings.Add(new KeyBinding(this.RetweetCommand, new KeyGesture(Key.W, ModifierKeys.Control)));
            this.InputBindings.Add(new KeyBinding(this.DeleteCommand, new KeyGesture(Key.Delete)));
            this.InputBindings.Add(new KeyBinding(this.DirectMessageCommand, new KeyGesture(Key.D, ModifierKeys.Control)));
        } //ViewModel Constructor

        #region private Fields

        private ObservableCollection<Tweet> _list = new ObservableCollection<Tweet>();
        private string _rateLimitStatus;
        private string _tweetMessage;
        private string _consumerKey = Settings.Default.ConsumerKey;
        private string _cosumerSecret = Settings.Default.ConsumerSecret;
        private string _accessKey { get { return Settings.Default.AccessToken; } }
        private string _accessSecret { get { return Settings.Default.AccessTokenSecret; } }
        private int _tweetsToGet = Settings.Default.TweetsToGet;
        private bool? _isHomeCheck = false;
        private bool? _isMentionCheck = false;
        private bool? _isYouCheck = false;
        private bool? _isDirectCheck = false;
        private bool home = false;
        private bool mention = false;
        private bool your = false;
        private bool direct = false;
        private bool _isTweetUpdating = false;
        private bool _isRefreshing = false;
        private bool _isRetweeting = false;
        private bool _verifiedCredentials = false;
        private Dispatcher _dispatcher;
        private DispatcherTimer _timer;
        private MainViewModel _model;
        private HomeCaller _hCaller;
        private YourCaller _yCaller;
        private MentionsCaller _mCaller;
        private DirectsCaller _dCaller;
        private Tweet _selectedItem;
        private Tweet _updatingFollowers;
        private Tweet UpdatingFollowersTweet
        {
            get
            {
                if (_updatingFollowers == null)
                    _updatingFollowers = new Tweet
                    {
                        User = "TwitBy",
                        Source = "TwitBy",
                        ID = "TwitBy",
                        Status = "Updating your followers so you can send direct messages to new followers since your last login.",
                        TimeAgo = DateTime.Now.ToString(),
                        ProfileImageURL = "Images/twitby.png"
                    };
                return _updatingFollowers;
            }
        }
        private long? _inReplyToId;

        #endregion

        #region public Properties

        public ICollection<Tweet> Tweets
        {
            get { return _list; }
            set
            {
                foreach (var item in value)
                    _list.Add(item);
            }
        }

        public string RateLimitStatus
        {
            get { return _rateLimitStatus; }
            set
            {
                _rateLimitStatus = value;
                OnPropertyChanged("RateLimitStatus");
            }
        }

        public string TweetMessage
        {
            get { return _tweetMessage; }
            set
            {
                _tweetMessage = value;
                OnPropertyChanged("TweetMessage");
                CheckIfReplyTo();
            }
        }

        public ICommand MentionCommand { get; private set; }

        public ICommand HyperlinkCommand {get; private set;}

        public ICommand HashtagCommand {get; private set;}

        public ICommand LoadHomeCommand {get; private set;}

        public ICommand LoadMentionsCommand { get; private set; }

        public ICommand LoadYourCommand { get; private set; }

        public ICommand LoadDirectsCommand { get; private set; }

        public ICommand UpdateCommand { get; private set; }

        public ICommand RefreshTimelineCommand { get; private set; }

        public ICommand GoToSettingsCommand { get; private set; }

        public ICommand LogOutCommand { get; private set; }

        public ICommand RetweetCommand { get; private set; }

        public ICommand DeleteCommand { get; private set; }

        public ICommand FavoriteCommand { get; private set; }

        public ICommand ReplyToCommand { get; private set; }

        public ICommand DirectMessageCommand { get; private set; }

        public ICommand UpdateFollowersCommand { get; private set; }

        public Tweet SelectedItem
        {
            get { return _selectedItem; }
            set
            {
                _selectedItem = value;
                OnPropertyChanged("SelectedItem");
            }
        }

        public DispatcherTimer Timer { get { return _timer; } }

        public bool? IsSelected { get; set; }

        public bool? IsHomeCheck
        {
            get { return _isHomeCheck; }
            set { _isHomeCheck = value; OnPropertyChanged("IsHomeCheck"); }
        }

        public bool? IsMentionCheck
        {
            get { return _isMentionCheck; }
            set { _isMentionCheck = value; OnPropertyChanged("IsMentionCheck"); }
        }

        public bool? IsYouCheck
        {
            get { return _isYouCheck; }
            set { _isYouCheck = value; OnPropertyChanged("IsYouCheck"); }
        }

        public bool? IsDirectCheck
        {
            get { return _isDirectCheck; }
            set { _isDirectCheck = value; OnPropertyChanged("IsDirectCheck"); }
        }

        public bool IsTweetUpdating
        {
            get { return _isTweetUpdating; }
            set
            {
                _isTweetUpdating = value;
                OnPropertyChanged("IsTweetUpdating");
            }
        }

        public bool IsRefreshing
        {
            get { return _isRefreshing; }
            set
            {
                _isRefreshing = value;
                OnPropertyChanged("IsRefreshing");
            }
        }

        public bool IsRetweeting
        {
            get { return _isRetweeting; }
            set
            {
                _isRetweeting = value;
                OnPropertyChanged("IsRetweeting");
            }
        }

        public bool VerifiedCredentials
        {
            get { return _verifiedCredentials; }
            set
            {
                _verifiedCredentials = value;
                OnPropertyChanged("VerifiedCredentials");
            }
        }

        public IList<InputBinding> InputBindings { get; private set; }

        public TimeSpan RefreshInterval
        {
            get
            {
                _timer.Interval = Settings.Default.RefreshInterval;
                return Settings.Default.RefreshInterval;
            }
        }

        #endregion

        #region private Methods

        private void LoadHomeTimeline()
        {
            _timer.Stop();
            _model.Title = "Home";

            if (home)
            {
                _hCaller.HomeTweets();
                _dCaller.DirectTweets();
                _mCaller.MentionTweets();
            }
            else
            {
                IsHomeCheck = true;
                home = true;
                mention = false;
                your = false;
                direct = false;

                _list.Clear();
                _hCaller.HomeTweets();
                _dCaller.DirectTweets();
                _mCaller.MentionTweets();
            }
            _timer.Start();
        }

        private void LoadMentionsTimeline()
        {
            _timer.Stop();
            _model.Title = "Mentions";

            if (mention)
                _mCaller.MentionTweets();
            else
            {
                IsMentionCheck = true;
                mention = true;
                home = false;
                your = false;
                direct = false;

                _list.Clear();
                _mCaller.MentionTweets();
            }

            _timer.Start();
        }

        private void LoadYourTweetsTimeline()
        {
            _timer.Stop();
            _model.Title = "Your tweets";

            if (your)
                _yCaller.YourTweets();
            else
            {
                IsYouCheck = true;
                your = true;
                direct = false;
                mention = false;
                home = false;

                _list.Clear();
                _yCaller.YourTweets();
            }

            _timer.Start();
        }

        private void LoadYourDirectsTimeline()
        {
            _timer.Stop();
            _model.Title = "Direct Messages";
            _list.Clear();

            if (direct)
                _dCaller.DirectTweets();
            else
            {
                IsYouCheck = true;
                direct = true;
                home = false;
                your = false;
                mention = false;

                _list.Clear();
                _dCaller.DirectTweets();
            }

            _timer.Start();
        }

        private void Mention(string phrase)
        {
            if (phrase.StartsWith("@"))
                if (string.IsNullOrEmpty(TweetMessage))
                    TweetMessage += phrase + " ";
                else
                    TweetMessage += phrase + " ";
            else
                if (string.IsNullOrEmpty(TweetMessage))
                    TweetMessage += "@" + phrase + " ";
                else
                    TweetMessage += "@" + phrase + " ";
        }

        private void GoToUrl(string uri)
        {
            System.Diagnostics.Process.Start(uri);
        }

        private void SearchForHash(string hash)
        {
            System.Diagnostics.Process.Start(hash);
        }

        private void SendTweet()
        {
            string WhaChaDoing;

            if (string.IsNullOrEmpty(TweetMessage))
            {
                MessageBox.Show("Tweet status cannot be empty!");
                return;
            }

            ITwitterLeafNode twitter;

            if (_inReplyToId.HasValue)
            {
                twitter = CreateRequest().Statuses().Update(TweetMessage).InReplyToStatus(_inReplyToId.Value).AsJson();
                WhaChaDoing = string.Format("Replying to {0}...", SelectedItem.User);
            }
            else
                if (TweetMessage.StartsWith("d "))
                {
                    var user = new Func<string, string>(p =>
                    {
                        int i = 2;
                        string s = null;
                        while (char.IsLetterOrDigit(p[i]) || p[i] == '_')
                        {
                            s += p[i];
                            i++;
                        }
                        return s;
                    }).Invoke(TweetMessage);

                    var message = new Func<string, int, string>((phrase, length) =>
                    {
                        string s = null;
                        for (int i = length + 3; i < phrase.Length; i++)
                            s += phrase[i];
                        return s;
                    }).Invoke(TweetMessage, user.Length);

                    twitter = CreateRequest().DirectMessages().Send(user, message).AsJson();

                    WhaChaDoing = string.Format("Sending Direct Message to {0}...", user);
                }
                else
                {
                    twitter = CreateRequest().Statuses().Update(TweetMessage).AsJson();
                    WhaChaDoing = "Updating...";
                }

            var aux = _dispatcher.BeginInvoke(new Func<string>(() =>
            {
                var s = TweetMessage;

                IsTweetUpdating = true;
                TweetMessage = WhaChaDoing;
                
                return s;
            }));

            var response = twitter.Request();

            if (response.IsTwitterError)
            {
                MessageBox.Show(response.AsError().ErrorMessage);
                TweetMessage = aux.Result.ToString();
                IsTweetUpdating = false;
                return;
            }

            _dispatcher.BeginInvoke(new Action(() =>
            {
                Refresh();

                TweetMessage = "";
                IsTweetUpdating = false;

            }));
        }

        private bool CanSendTweet()
        {
            if (string.IsNullOrEmpty(TweetMessage))
                return false;
            else
                if (IsTweetUpdating)
                    return false;
                else
                    return true;
        }

        private bool CanRefresh()
        {
            if (IsRefreshing)
                return false;
            else
                return true;
        }

        private bool CanDelete(Tweet tweet)
        {
            if (SelectedItem == null)
                return false;
            if (tweet.User == Properties.Settings.Default.UserId)
                return true;
            else
                return false;
        }

        private bool CanDirectMessage()
        {
            if (SelectedItem == null)
                return false;
            if (SelectedItem.User != Settings.Default.UserId && Settings.Default.Followers.Contains(SelectedItem.User))
                return true;
            else
                return false;
        }

        private bool CanUseToolbar()
        {
            if (SelectedItem == null || SelectedItem.IsDirect || SelectedItem.ID == "TwitBy" || SelectedItem.ID == "Mention" || SelectedItem.ID=="Sent" || SelectedItem.ID=="Direct" || SelectedItem.ID=="Home")
                return false;
            else
                return true;
        }

        private void LogOut()
        {
            Settings.Default.AccessToken = "";
            Settings.Default.AccessTokenSecret = "";
            _list.Clear();
            _list.Add(new Tweet
            {
                TimeAgo = DateTime.Now.ToString(),
                ID = "TwitBy",
                Source = "TwitBy",
                User = "TwitBy",
                ProfileImageURL = "Images/twitby.png",
                Status = "You've been logged out. Please restart TwitBy."
            });
            IsRefreshing = true;
            IsTweetUpdating = true;
        }

        private void Retweet(Tweet tweet)
        {
            var rtStatus = string.Format("RT @{0}: {1}", tweet.User, tweet.Status);
            var statusLength = rtStatus.Length;
            IsRetweeting = true;
            if (statusLength >= 140)
            {
                rtStatus = rtStatus.Remove(rtStatus.Length - (tweet.User.Length + 9));
                rtStatus += "...";
                TweetMessage = rtStatus;
            }
            else
                TweetMessage = rtStatus;
        }

        private void Delete(Tweet tweet)
        {
            var twitter = CreateRequest().Statuses().Destroy(long.Parse(tweet.ID)).AsJson();

            if (MessageBox.Show(string.Format("Delete this tweet:\n\n{0}\n\n?", tweet.Status), "Confirm Delete", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
            {
                Tweets.Remove(tweet);
                twitter.CallbackTo(TweetModded).BeginRequest();
            }
            else
                return;
        }

        private void Favorite(Tweet tweet)
        {
            if (tweet.IsFavorite)
            {
                SelectedItem.IsFavorite = false;

                var twitter = CreateRequest().Favorites().Unfavorite(long.Parse(tweet.ID)).AsJson();

                twitter.CallbackTo(TweetModded).BeginRequest();
            }
            else
            {
                SelectedItem.IsFavorite = true;

                var twitter = CreateRequest().Favorites().Favorite(long.Parse(tweet.ID)).AsJson();

                twitter.CallbackTo(TweetModded).BeginRequest();
            }
        }

        private void DirectMessage(Tweet tweet)
        {
            TweetMessage = string.Format("d {0} ", tweet.User);
        }

        private void ReplyTo(Tweet tweet)
        {
            CheckIfReplyTo();
            TweetMessage = "@" + tweet.User + " ";
        }

        private void UpdateFollowers()
        {
            if (!VerifiedCredentials)
            {
                _dispatcher.BeginInvoke(new Action(() =>
                {
                    Tweets.Add(UpdatingFollowersTweet);
                }));

                var twitter = CreateRequest()
                    .Users().GetFollowers()
                    .AsJson().CallbackTo(GotFollowers).BeginRequest();
            }
            else
                _dispatcher.BeginInvoke(new Action(() =>
                {
                    home = true;
                    LoadHomeCommand.Execute(null);
                }));
        }

        #endregion

        #region Helpers

        private IFluentTwitter CreateRequest()
        {
            return FluentTwitter.CreateRequest()
                            .AuthenticateWith(_consumerKey, _cosumerSecret, _accessKey, _accessSecret);
        }

        private void _timer_Tick(object sender, EventArgs e)
        {
            Refresh();
        }

        private void Refresh()
        {
            if (IsRefreshing)
                return;
            else
            {
                if ((bool)IsHomeCheck)
                    LoadHomeCommand.Execute(null);
                if ((bool)IsMentionCheck)
                    LoadMentionsCommand.Execute(null);
                if ((bool)IsYouCheck)
                    LoadYourCommand.Execute(null);
                if ((bool)IsDirectCheck)
                    LoadDirectsCommand.Execute(null);
            }
            if (_timer.Interval != Settings.Default.RefreshInterval)
                _timer.Interval = Settings.Default.RefreshInterval;
        }

        private void TweetModded(object sender, TwitterResult result, object userState)
        {
            if (result.IsFailWhale || result.TimedOut)
                MessageBox.Show("It appears that there's a network connection error or Twitter might be down.", "Error", MessageBoxButton.OK);
            else
                if (result.IsTwitterError)
                    MessageBox.Show(result.AsError().ErrorMessage, "Error", MessageBoxButton.OK);
                else
                    return;
        }

        private void CheckIfReplyTo()
        {
            long number;
            if (SelectedItem == null)
            {
                _inReplyToId = null;
                return;
            }
            if (long.TryParse(SelectedItem.ID, out number))
                if (_inReplyToId != long.Parse(SelectedItem.ID))
                    _inReplyToId = long.Parse(SelectedItem.ID);
            if (string.IsNullOrEmpty(TweetMessage) || TweetMessage.StartsWith("RT") || !(TweetMessage.Contains(string.Format("@{0}",SelectedItem.User))))
                _inReplyToId = null;
        }

        private void GotFollowers(object sender, TwitterResult result, object userState)
        {
            if (result.IsTwitterError)
                UpdateFollowersCommand.Execute(null);
            else
                if (result.IsFailWhale || result.TimedOut)
                    UpdateFollowersCommand.Execute(null);
                else
                {
                    var users = result.AsUsers();

                    if (users != null)
                    {
                        Settings.Default.Followers = new System.Collections.Specialized.StringCollection();
                        foreach (var item in users)
                            Settings.Default.Followers.Add(item.ScreenName);
                        VerifiedCredentials = true;
                    }
                    else
                    {
                        _dispatcher.BeginInvoke(new Action(() =>
                        {
                            Tweets.Remove(UpdatingFollowersTweet);
                        }));

                        UpdateFollowersCommand.Execute(null); //in theory should retry to infinty
                        return;
                    }

                    _dispatcher.BeginInvoke(new Action(() =>
                    {
                        Tweets.Remove(UpdatingFollowersTweet);
                        _updatingFollowers = null;

                        LoadHomeCommand.Execute(null);
                    }));
                }
        }

        #region INotifyPropertyChanged Members

        public override event PropertyChangedEventHandler PropertyChanged;

        public override void  OnPropertyChanged(string propName)
        {
            if(PropertyChanged!=null)
                PropertyChanged(this,new PropertyChangedEventArgs(propName));
        }

        #endregion

        #endregion

    }
}
